// Copyright (c) Symbian Ltd 2008. All rights reserved.

#include "rpsgamescreens.h"
#include "BluetoothManager.h"
#include "BluetoothDeviceDiscoverer.h"
#include "BluetoothConnector.h"
#include "common.hrh"

// -----------------------------------------------------------------------------------
// CBluetoothManager class
// -----------------------------------------------------------------------------------
CBluetoothManager* CBluetoothManager::NewL(CGameScreenManager& aGameScreenMgr)
	{
	CBluetoothManager* me = new (ELeave) CBluetoothManager(aGameScreenMgr);
	CleanupStack::PushL(me);
	me->ConstructL();
	CleanupStack::Pop(me);
	return (me);
	}

void CBluetoothManager::ConstructL()
	{
	// Get socket server session
	User::LeaveIfError(iSocketServer.Connect());
	iBtDeviceDiscoverer = CBluetoothDeviceDiscoverer::NewL(*this);
	}

CBluetoothManager::CBluetoothManager(CGameScreenManager& aGameScreenMgr)
: iGameScreenMgr(aGameScreenMgr)
	{
	TUUID targetServiceClass(KRPS_BTServiceID);
	//Use by the RNotifier dialog to get only BT devices that supports the RPS's service
	iDevSelFilter.SetUUID(targetServiceClass);
	}

CBluetoothManager::~CBluetoothManager()
	{
	if(iBtConnBase)
		{		
		iBtConnBase->Cancel();
		delete iBtConnBase;
		}
	
	if (iBtDeviceDiscoverer)
		{
		iBtDeviceDiscoverer->Cancel();
		delete iBtDeviceDiscoverer;
		}
	
	iSocketServer.Close();
	
	//iBtConnBaseArray.ResetAndDestroy();//Not used for 2 player multiplay, see header file for details
	}

/*
============================================================================
Callback from the Control Screen. The player has decided to control the game.
The CRpsGameEngine takes the Master role of the game.
============================================================================
*/
void CBluetoothManager::StartBtMaster()
	{
	iGameScreenMgr.GameData().iRpsError = KErrNone;
	
	//Display the Connecting screen
	iGameScreenMgr.SetGameState(CGameScreenManager::ENotifierScreen);
	
	//Start BT device discovery
	TBTDeviceSelectionParamsPckg pckg(iDevSelFilter);
	TRAPD(err, iBtDeviceDiscoverer->DiscoverAndSelectDeviceL(pckg));
	if(err!=KErrNone)
		{
		DisplayError(err);
		}
	}

/*
============================================================================
Callback from the Control Screen. The player has decided to wait for a connection.
The CRpsGameEngine takes the Slave role of the game.
============================================================================
*/
void CBluetoothManager::StartBtSlave()
	{
	iGameScreenMgr.GameData().iRpsError = KErrNone;
	
	//Display the Waiting Screen
	iGameScreenMgr.SetGameState(CGameScreenManager::EWaitConnScreen);
	
	ASSERT(!iBtConnBase);
	
	TRAPD(err, iBtConnBase = CBluetoothResponder::NewL(*this, iSocketServer);
			   iBtConnBase->StartL());

	if(err != KErrNone)
		{
		if(iBtConnBase)
			{
			delete iBtConnBase;
			iBtConnBase = NULL;
			}
		DisplayError(err);			
		}
	}

/*
============================================================================
Quit the game either because the player decided to exit the game (KErrNone)
or because an error occurred during the game		   			   
============================================================================
*/
void CBluetoothManager::QuitMultiplayerGame()
	{
	iConnected = EFalse;

	if(iBtConnBase)
		{			
		iBtConnBase->Cancel();
		delete iBtConnBase;
		iBtConnBase = NULL;
		}
	}


/*
============================================================================
Master kick off the game.
============================================================================
*/
void CBluetoothManager::StartGame()
	{
	//We assume only the Master can start the game since it's the one that controls the game.
	ASSERT(iBtConnBase);
	ASSERT(iBtConnBase->Master());

	if(iConnected)
		{
		//Send the control flag EPlay to the slave asking to start the game	
		TPckgBuf<TRpsData> rpsData;
		rpsData().iRpsControl = TRpsData::EPlay;
		iBtConnBase->SendData(rpsData);
		// Reset the game data before play starts
		iGameScreenMgr.Roshambo().ResetPlayers();
		//Show the play screen
		iGameScreenMgr.SetGameState(CGameScreenManager::EPlayScreen);
		}
	}

/*
============================================================================
Start another session of the game.
============================================================================
*/
void CBluetoothManager::ReplayGame()
	{
	//Master and Slave restart the game
	ASSERT(iBtConnBase);	
	if(iConnected)
		{
		if(iBtConnBase->Master())
			{
			//Master displays the Start Screen
			iGameScreenMgr.SetGameState(CGameScreenManager::EStartScreen);	
			}
		else
			{
			//Slave displays the Wait Start Screen
			iGameScreenMgr.SetGameState(CGameScreenManager::EWaitStartScreen);		
			}
		}
	}

/*
============================================================================
Send the RPS's player choice to the remote BT device. On completion calls SendDataComplete()
============================================================================
*/
void CBluetoothManager::SendData(TRoshambo::TElement aElement)
	{
	//Note that the code here to send the data to the remote device is the same for the 
	//Master and slave. Change the code to be like ReplayGame() above if your multiplayer game needs to handle
	//the Master and Slave differently.  	
	if(iConnected)
		{
		TPckgBuf<TRpsData> rpsData;
		rpsData().iElement = aElement;
		rpsData().iRpsControl = TRpsData::EElement;

		//Since the sending is an asynchronous operation we need to cache the local player's choice
		iGameScreenMgr.Roshambo().SetLocalPlayer(aElement);
		// Now wait for the opponent to choose
		iGameScreenMgr.SetGameState(CGameScreenManager::EWaitOpponentScreen);
		iBtConnBase->SendData(rpsData);
		}
	}

/*
============================================================================
Query function to see if Master/Slave are connected 
============================================================================
*/
TBool CBluetoothManager::Connected()
	{
	return iConnected;	
	}

/*
============================================================================
RNotifier's callback. aResponse contains the player's selected BT device
============================================================================
*/
void CBluetoothManager::OnDeviceDiscoveryComplete(const TBTDeviceResponseParamsPckg& aResponse)
	{
	iGameScreenMgr.SetGameState(CGameScreenManager::EConnectingScreen);
	iBtDevAddr = aResponse().BDAddr();
	
	if(iBtConnBase)
		{
		iBtConnBase->Cancel();
		delete iBtConnBase;
		iBtConnBase = NULL;
		}
	
	//It's important to note that in a multiplayer game with more then two players, the engine needs to create an
	//array of iBtConnBase, one for each Master<->Slave connection in the BT piconet. The BT piconet contains
	//the BT devices (Slaves) selected by the player (Master). RHostResolver needs to be use instead of RNotifier 
	//if your application needs to select more then two BT devices at one time. A customized UI needs also to be
	//implemented to show the player and the available BT devices (s)he can select.The engine needs also to manage
	//the bidirectional communication between the Master<->Slaves and SlaveN<->Master<->SlaveM.
  
	TRAPD(err, StartConnectorL());
	if(err != KErrNone)
		{
		delete iBtConnBase;
		iBtConnBase = NULL;
		DisplayError(err);
		}		
	}

void CBluetoothManager::StartConnectorL()
	{
	iBtConnBase = CBluetoothConnector::NewL(*this, iSocketServer, iBtDevAddr);
    iBtConnBase->StartL();
	}
/*
============================================================================
Callback from CBluetoothDeviceDiscoverer if an error occur during device discovery
============================================================================
*/
void CBluetoothManager::OnDeviceDiscoveryErr(const TInt aError)
	{
	DisplayError(aError);
	}

/*
============================================================================
Callback either from the Connector or Responder to report a connection error
============================================================================
*/
void CBluetoothManager::ConnectionErr(const TInt /*aConnHandle*/, const TInt aError)
	{
	//See RpsGameEngine.h to see how your multiplayer game could use aConnHandle
	if(aError == KErrNone)
		{
		//KErrNone means succesful connection
		iConnected = ETrue;
		//Master and Slave need to take different action at this point
		ASSERT(iBtConnBase);
		if(iBtConnBase->Master())
			{			
			iGameScreenMgr.SetGameState(CGameScreenManager::EStartScreen);
			}
		else
			{
			iGameScreenMgr.SetGameState(CGameScreenManager::EWaitStartScreen);	
			}
		}
	else
		{
		QuitMultiplayerGame();
		DisplayError(aError);
		}
	}

/*
============================================================================
Callback either from the Connector or Responder to report incoming data from the remote BT device
============================================================================
*/
void CBluetoothManager::DataReceived(const TInt /*aConnHandle*/, const TDesC8& aData)
	{
	//See RpsGameEngine.h to see how your multiplayer game could use aConnHandle
	TPckgBuf<TRpsData> rpsData;
	rpsData.Copy(aData);
	if(rpsData().iRpsControl == TRpsData::EPlay)
		{
		//We assume that only the Responder deals with game's control flags.
		ASSERT(!iBtConnBase->Master());
		iGameScreenMgr.Roshambo().ResetPlayers();
		//Start the game by displaying the Play Screen
		iGameScreenMgr.SetGameState(CGameScreenManager::EPlayScreen);
		}
	else if(rpsData().iRpsControl == TRpsData::EElement)
		{
		TPckgBuf<TRpsData> rpsData;
		rpsData.Copy(aData);
		if(iGameScreenMgr.Roshambo().LocalPlayer() != TRoshambo::EInvalid)
			{
			//Player already sent the rps's element, display the result.
			iGameScreenMgr.Roshambo().SetOpponent(rpsData().iElement);	
			iGameScreenMgr.SetGameState(CGameScreenManager::EResultScreen);
			}
		else
			{
			//Cache the opponent element and wait for the player
			iGameScreenMgr.Roshambo().SetOpponent(rpsData().iElement);
			}
		}
	}

/*
============================================================================
Callback either from the Connector or Responder to report the sending data completion
============================================================================
*/
void CBluetoothManager::SendDataComplete(const TInt /*aConnHandle*/)
	{		
	//See RpsGameEngine.h to see how your multiplayer game could use aConnHandle
	//We already cached the local player's move
	if(iGameScreenMgr.Roshambo().Opponent() != TRoshambo::EInvalid)
		{
		//Opponent already submitted the move, so result can be calculated and displayed
		iGameScreenMgr.SetGameState(CGameScreenManager::EResultScreen);
		}
	}

void CBluetoothManager::DisplayError(TInt aError)
	{
	iGameScreenMgr.GameData().iRpsError = aError;
	iGameScreenMgr.SetGameState(CGameScreenManager::EErrorScreen);	
	}

